<?php

namespace bee;
define("GRID_DATASOURCE_ARRAY",   1);
define("GRID_DATASOURCE_DAO",     2);
define("GRID_FETCH_ALL",          4);
define("GRID_FETCH_PAGED",        8);
define("GRID_FILL_TABLE",         16);
define("GRID_FILL_VARIABLES",     32);
define("GRID_FILL_FILTER",        64);
define("GRID_FILL_ALL",           112);

define("GRID_SHOW_NONE",          0);
define("GRID_SHOW_EDIT",          1);
define("GRID_SHOW_DELETE",        2);
define("GRID_SHOW_SHOW",          4);
define("GRID_SHOW_ALL",           255);
/**
 *
 * @author dzelenika
 *
 */
abstract class grid extends singleton
{ protected static $__instance;
  protected $__gridID = "grd200911151730";
  protected $__dispatcher;
  /**
   * @var XMLDataHub
   */
  protected $__DSDoc;
  protected $__numRows = 0;
  protected $__foreignKeys;
  protected $foreignKeys;
  protected $state;
  //protected $dispatcher;
  public    $DAORow;
  public    $afterWhere;
  //public    $not_filter  = array("sys_page", "sys_action", "PHPSESSID", "XDEBUG_SESSION", "sys_stateID", "app_back", "username", "__form");//, "grid_step");
  public    $fetchType   = GRID_FETCH_PAGED;
  public    $options;
  protected $gridStep;
  protected $ffwdStep;
  protected $tableName;

  protected $columnProperties;
  public    $detailFormName;
  public    $detailLinkField = "id";
  public    $showButtons = GRID_SHOW_ALL;
  protected $extraButtons;
  public    $caption;

  protected static $loaded = false;

  public function __wakeup()
  { //echo "<br />WAKE $this->__gridID " . get_called_class();


    if($this)
    {
      $this->__dispatcher = dispatcher::getInstance();
      $this->__dispatcher->addObject($this);
      //$this->__DSDoc = XMLDataHub::getInstance();
    }
    //$this->__dispatcher->dispatch("onLoad");

  }

/**
 * @param bool $noLoad
 * @return grid
 */
  static function getInstance($noLoad = true)
  { $className = get_called_class();
    if(!is_subclass_of(static::$__instance, $className))//self))
    { static::$__instance = new $className($noLoad);
    } else
    { if(!static::$loaded)
      { static::$loaded = true;
      }
    }
    $d = dispatcher::getInstance();
    $d->dispatch("onLoad");
    return static::$__instance;
  }

  protected function __construct($noLoad = true)
  { $this->__dispatcher    = dispatcher::getInstance();
    $this->__dispatcher->addObject($this);
    $this->options = GRID_FETCH_PAGED | GRID_FILL_ALL;
    $this->extraButtons        = array();


    if($noLoad)
    { $this->__dispatcher->dispatch("createGrid");
    } elseif(static::$__instance = factory::load($this->__gridID))
    {
    } else
    { //$this->__dispatcher->dispatch("createGrid"); // not tested
    }
    $this->__withEvents        = true;
  	$this->state               = new oArray();
  	//$this->__DSDoc             = XMLDataHub::getInstance();
  }

  public function getGridStep()
  { return $this->gridStep ?: config::getInstance()->app_GRID_STEP;
  }

  public function setGridStep($step)
  { $this->gridStep = $step;
  }

  public function getFFWDStep()
  { return $this->ffwdStep ?: config::getInstance()->app_FFWD_STEP;
  }

  public function setFFWDStep($step)
  { $this->ffwdStep = $step;
  }

  /**
   *
   * Enter description here ...
   * @return string
   */
  public function getTableName()
  { if($this->tableName)
    { return $this->tableName;
    }
    if(is_subclass_of($this->DAORow, "bee\DAO"))
    {  return $this->DAORow->getSysTable();
    }
    return "__grid__table__";
  }
  public function setTableName($tName)
  {  $this->tableName = $tName;
  }

 /**
 *
 * @param unknown_type $evt
 */
  public function createGrid($evt)
  {
  }
	 /**
   *
   * @param $evt
   * @return void
   */
  public function onUnLoad($evt)
  {
  }

   /**
   *
   * @param $evt
   * @return void
   */
  public function onLoad($evt)
  {
  }

  public function onDraw($evt)
  { $gridXPath = "/form/widgets/" . $this->__gridID;
    $__DSDoc = XMLDataHub::getInstance();
    $__DSDoc->setNode("/form/widgets", $this->__gridID, $this);
    $__DSDoc->setNode($gridXPath, "id", $this->__gridID);
    $this->fillXML();
    if($this->columnProperties)
    { $__DSDoc->setNode($gridXPath, "columns", $this->columnProperties);
    }

    $__DSDoc->setNode($gridXPath, "page_no",   $this->state->getField("grid_page_no") ?: 0);
    $__DSDoc->setNode($gridXPath, "grid_step", $this->getGridStep());
    $__DSDoc->setNode($gridXPath, "ffwd_step", $this->getFFWDStep());
    $__DSDoc->setNode($gridXPath, "num_rows", $this->__numRows);
    $__DSDoc->setNode($gridXPath, "extraButtons", $this->extraButtons);
  }

	public function setDataSource($dataDource, afterWhere $afterWhere = null)
	{ $this->DAORow = $dataDource;
	  $this->afterWhere =   ($afterWhere ?: new afterWhere());
	  $this->resetGrid();
	}

	public function resetGrid()
	{ $this->state->setField("grid_order_type", "");
    $this->state->setField("grid_order", "");
    $this->state->setField("SQLOrder", "");
    $this->state->setField("grid_order_field", "");
    $this->state->setField("grid_page_no", 0);
	}

	public function setForeignKey($field, array $FKArray)
	{  $this->foreignKeys[$field] = $FKArray;
	}

	public function getNumRows()
	{ if(is_array($this->DAORow))
	  { return count($this->DAORow);
	  } else if(is_subclass_of($this->DAORow, "bee\\DAO"))
	  { return $this->__numRows;
	  } else
	  { return 0;
	  }
	}

	public function grid_next($evt)
	{ //echo "++" . $this->state->getField("grid_page_no") . "++";
	  $step = $this->getGridStep();// config::getInstance()->app_GRID_STEP;
	  $this->state->setField("grid_page_no", $this->state->getField("grid_page_no") + 1);
     if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $step) //????????????????????????????????????????? istestirati
     { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $step)) );//?????????????????????????????????????????
     }
     //die("++" . $this->state->getField("grid_page_no") . "++");
	}

  public function grid_prev($evt)
  { $this->state->setField("grid_page_no", $this->state->getField("grid_page_no") - 1);
    if($this->state->getField("grid_page_no") < 0)
    { $this->state->setField("grid_page_no", 0);
    }
  }

  public function grid_change_order($evt)
  { $this->state->setField("grid_order_type", isset($evt["grid_order_type"]) ? $evt["grid_order_type"] : "text");
    if($evt["grid_change_field"] == $this->state->getField("grid_order_field"))
    { if($this->state->getField("grid_order") == "descending")
      { $this->state->setField("grid_order", "ascending");
        $this->state->setField("SQLOrder", "ASC");
      } else
      { $this->state->setField("grid_order", "descending");
        $this->state->setField("SQLOrder", "DESC");
      }
    } else
    { $this->state->setField("grid_order", "ascending");
      $this->state->setField("SQLOrder", "ASC");
      $this->state->setField("grid_order_field", $evt["grid_change_field"]);
      $this->state->setField("grid_page_no", 0);
    }
  }

  public function grid_set_order($evt)
  { $this->state->setField("grid_order_type", isset($evt["grid_order_type"]) ? $evt["grid_order_type"] : ($this->state->getField("grid_order_type")?: "text"));
    if(($this->state->getField("grid_order_field") == $evt["grid_order_field"]) || isset($evt["ignore_change"]))
    { $this->state->setField("grid_order", $evt["grid_order"]);
    } else
    { $this->state->setField("grid_order", "ascending");
    }

    $this->state->setField("SQLOrder", ($this->state->getField("grid_order") == "descending") ? "DESC" : "ASC");
    $this->state->setField("grid_order_field", $evt["grid_order_field"]);
    $this->state->setField("grid_page_no", 0);
  }

  public function grid_move($evt)
  { $step = $this->getGridStep();
  	$this->state->setField("grid_page_no", $this->state->getField("grid_page_no") + @$evt["grid_step"]);
    if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $step)
    { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $step)) );
    }
    if($this->state->getField("grid_page_no") < 0)
    { $this->state->setField("grid_page_no", 0);
    }
  }

  public function grid_goto($evt)
  {
    $step = $this->getGridStep();
    $this->state->setField("grid_page_no", $evt["page_no"]);
    if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $step)
    { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $step)) );
    }
    if($this->state->getField("grid_page_no") < 0)
    { $this->state->setField("grid_page_no", 0);
    }
  }

  public function grid_first($evt)
  { $this->state->setField("grid_page_no", 0);
  }

  public function grid_last($evt)
  { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $this->getGridStep())));//????
  }
  public function grid_FFWD($evt)
  { $step = config::getInstance()->app_FFWD_STEP;
  	$this->state->setField("grid_page_no", $this->state->getField("grid_page_no") + $step);
     if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $this->getGridStep())
     { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $this->getGridStep())) );//?????????????????????????????????????????
     }
  }

  public function grid_FBWD($evt)
  { $step = config::getInstance()->app_FFWD_STEP;
    $this->state->setField("grid_page_no", $this->state->getField("grid_page_no") - $step);
     if($this->state->getField("grid_page_no") < 0)
     { $this->state->setField("grid_page_no", 0);
     }
  }

  /**
   * @deprecated - for removal ...
   */
  /*
  public function grid_filter($evt)
  { $this->state->setField("grid_page_no", 0);
    foreach ($evt as $key => $value)
    { if(!in_array($key, $this->not_filter))
      { if($value)
        { $this->state->setField("filter_" . $key, $value);
        } else
        { $this->state->removeField("filter_" . $key);
        }
      }
    }
  }
*/

  public function fillXML($options = null, $table_name = "")// $fillTable = true, $fillVariables = true, $setFilter2DS = true)
  {	$cfg           = config::getInstance();
	  $afWh          = null;
	  $first_row     = 0;
	  $last_row      = 0;

	  $options       = $options ?: $this->options;
	  $table_name    = $table_name ?: $this->getTableName();
    if(($options & GRID_FETCH_ALL) == GRID_FETCH_ALL)
    { $this->fetchType |= GRID_FETCH_ALL;
    } else if(($options & GRID_FETCH_PAGED) == GRID_FETCH_PAGED)
    { $this->fetchType |= GRID_FETCH_PAGED;
    }

	  if(is_array($this->DAORow))
    { $this->__numRows = count($this->DAORow);
    }
	  if(($this->fetchType & GRID_FETCH_PAGED) == GRID_FETCH_PAGED)
    { if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $this->getGridStep())
      { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $this->getGridStep())) );
      }
      $first_row  = (int)$this->state->getField("grid_page_no") * $this->getGridStep() + 1;
      $last_row   = ($this->state->getField("grid_page_no") + 1)* $this->getGridStep();
      if($this->afterWhere)
      { $afWh = clone $this->afterWhere;
      }
      if(is_array($this->DAORow))
      { $new_arr = array();
    	  if($this->state->getField("grid_order_field"))
        { usort($this->DAORow, array("self", "sortArray"));
        }
        $i = 0;
        foreach($this->DAORow as $row)
        { if($i >= $first_row - 1 && $i < $last_row)
          { $new_arr[] = $row;
          }
          $i++;
        }
      } else
      { if($this->state->getField("grid_order_field"))
        { $afWh->text .= " ORDER BY " . filter_var($this->state->getField("grid_order_field") . " " . $this->state->getField("SQLOrder"),
                                            FILTER_VALIDATE_REGEXP, array("options" =>array("regexp" => "/([a-z]|[A-Z]|[0-9]|_)*/")));
        }
      }
    }

    if(is_subclass_of($this->DAORow, "bee\DAO"))
    { $DR = $this->DAORow;//->__deepCopy();
	    $rs = new rowSet($DR, $afWh, (int)$this->state->getField("grid_page_no") * $this->getGridStep() + 1, ($this->state->getField("grid_page_no") + 1) * $this->getGridStep(), $s);
      $this->__numRows = $rs->num_rows;

	    if($this->state->getField("grid_page_no") > ($this->__numRows - 1) / $this->getGridStep())
      { $this->state->setField("grid_page_no", ( (int)(floor($this->__numRows - 1) / $this->getGridStep())) );
        $first_row  = (int)$this->state->getField("grid_page_no") * $this->getGridStep() + 1;
        $last_row   = ($this->state->getField("grid_page_no") + 1)* $this->getGridStep();
        $rs = new rowSet($DR, $afWh, (int)$this->state->getField("grid_page_no") * $this->getGridStep() + 1, ($this->state->getField("grid_page_no") + 1) * $this->getGridStep(), $s);
      }
	  }
    $__DSDoc = XMLDataHub::getInstance();
	  if(($options & GRID_FILL_TABLE) == GRID_FILL_TABLE)
	  { if(is_array($this->DAORow))
      { $__DSDoc->setTableFromArr(isset($new_arr) ? $new_arr : $this->DAORow, $table_name, $this->foreignKeys);
        //$DSDoc->setObject("/form/widgetes/" . static::$__gridID . "/data", $new_arr);//???
      } else if(is_subclass_of($this->DAORow, "bee\DAO"))
      {
        //$DSDoc->setTable(clone $rs, $table_name ?: $rs->getTableName(), $this->foreignKeys);
        $class_name = get_class($this->DAORow);
        $row = new $class_name();
        while($rs->getRow($row))
        { $__DSDoc->setObject("/form/widgets/" . $this->__gridID . "/data/row", $row, false);
        //echo "<br />ROW: " . print_r($row, true);
        }
      }
    }

    if(($options & GRID_FILL_VARIABLES) == GRID_FILL_VARIABLES)
    { $__DSDoc->setVariable("grid_order", $this->state->getField("grid_order") ?: "ascending");
	    $__DSDoc->setVariable("grid_anti_order", ($this->state->getField("grid_order") == "descending")? "ascending": "descending");
  	  $__DSDoc->setVariable("grid_order_field", $this->state->getField("grid_order_field") ?: "");
  	  $__DSDoc->setVariable("grid_order_type", $this->state->getField("grid_order_type") ?: "text");

  	  //$DSDoc->setVariable("grid_page_no", $this->state->getField("grid_page_no") ?: 0);
  	  //$DSDoc->setVariable("app_grid_step", $this->getGridStep());
  	  //$DSDoc->setVariable("app_FFWD_STEP", $this->getGridStep());
  	/**
  	 * @todo riješiti se jednog od donja 2
  	 */
  	  //$DSDoc->setVariable("grid_total_rows", $this->__numRows);
  	  //$DSDoc->setVariable("grid_num_rows", $this->__numRows);
	  }

    if(($options & GRID_FILL_FILTER) == GRID_FILL_FILTER)
    { foreach($this->state->arr_val as $key => $value)
      { if(substr($key, 0, 7) == "filter_")
        { $__DSDoc->setVariable( $key, $value);
        }
      }
    }
  }

	/**
	 *
	 * @param array $e1
	 * @param array $e2
	 * @return int
	 */
	private function sortArray($e1, $e2)
	{ $sort_type = $this->state->getField("grid_order_type") ?: "text";
		if($field = $this->state->getField("grid_order_field"))
	  { if(isset($this->foreignKeys[$field]))
	    {  $e1[$field] = isset($this->foreignKeys[$field][$e1[$field]]) ? $this->foreignKeys[$field][$e1[$field]] : "*";
	       $e2[$field] = isset($this->foreignKeys[$field][$e2[$field]]) ? $this->foreignKeys[$field][$e2[$field]] : "*";
	    }

	  	if(strcmp($e1[$field], $e2[$field]) < 0 && $sort_type == "text" || (int)$e1[$field] < (int)$e2[$field] && $sort_type == "number")
	    { if(($this->state->getField("SQLOrder") ?: "ASC") == "ASC")
	      { $return = -1;
	      } else
	      { $return = 1;
	      }
	    } else if(strcmp($e1[$field], $e2[$field]) > 0 && $sort_type == "text" || (int)$e1[$field] > (int)$e2[$field] && $sort_type == "number")
	    { if(($this->state->getField("SQLOrder") ?: "ASC") == "ASC")
        { $return = 1;
        } else
        { $return = -1;
        }
	    } else
	    { $return = 0;
	    }
	  } else
	  { $return = 0;
	  }
	  return $return;
	}

  private function filterArray($value)
  { $sort_type = $this->state->getField("grid_order_type") ?: "text";
    if($field = $this->state->getField("grid_order_field"))
    { if(isset($this->foreignKeys[$field]))
      { $e1[$field] = isset($this->foreignKeys[$field][$e1[$field]]) ? $this->foreignKeys[$field][$e1[$field]] : "*";
        $e2[$field] = isset($this->foreignKeys[$field][$e2[$field]]) ? $this->foreignKeys[$field][$e2[$field]] : "*";
      }
      if(($e1[$field] < $e2[$field] && $sort_type == "text") || (int)$e1[$field] < (int)$e2[$field])
      { if(($this->state->getField("SQLOrder") ?: "ASC") == "ASC")
        { return -1;
        } else
        { return 1;
        }
      } else if(($e1[$field] > $e2[$field] && $sort_type == "text") || (int)$e1[$field] > (int)$e2[$field])
      { if(($this->state->getField("SQLOrder") ?: "ASC") == "ASC")
        { return 1;
        } else
        { return -1;
        }
      } else
      { return 0;
      }
    } else
    { return 0;
    }
  }

  public function setColumnProperties(array $properties)
  { if(isset($properties["name"]))
    { $this->columnProperties[$properties["name"]] = new gridColumnProperties($properties);
    } else
    { $this->columnProperties[] = new gridColumnProperties($properties);
    }
  }
  public function addExtraButton(extraButton $button)
  { $this->extraButtons[] = $button;
  }
}

class gridColumnProperties
{ public $name;
  public $caption;
  public $width;
  public $orderType;
  public $CSSclass;

  public function __construct(array $properties)
  { foreach($properties as $key => $value)
    { $this->$key = $value;
    }
  }
}

class extraButton
{ public $pageType;
  public $pageName;
  public $action;
  public $useId;
  public $text;

  public function __construct(array $properties)
  { foreach($properties as $key => $value)
    { $this->$key = $value;
    }
  }
}